package edu.lingnan.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.util.BeanUtil;
import edu.lingnan.annotation.Log;
import edu.lingnan.aop.LogAspect;
import edu.lingnan.constant.LogTypeEnum;
import edu.lingnan.dto.StudentCheckReq;
import edu.lingnan.dto.StudentPasswordReq;
import edu.lingnan.dto.StudentQueryReq;
import edu.lingnan.dto.StudentReq;
import edu.lingnan.dto.result.StudentBookingInfo;
import edu.lingnan.dto.result.StudentLoginNoBookingInfo;
import edu.lingnan.entity.Booking;
import edu.lingnan.entity.ClassRoom;
import edu.lingnan.entity.Seat;
import edu.lingnan.entity.Student;
import edu.lingnan.service.BookingService;
import edu.lingnan.service.ClassRoomService;
import edu.lingnan.service.SeatService;
import edu.lingnan.service.StudentService;
import edu.lingnan.util.MD5Util;
import edu.lingnan.vo.LogStudentVo;
import edu.lingnan.vo.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

@Api(tags = "学生控制器")
@RestController
@Slf4j
public class StudentController {


    @Autowired
    private PlatformTransactionManager transactionManager;


    @Resource
    private StudentService studentService;
    @Resource
    private BookingService bookingService;
    @Resource
    private ClassRoomService classRoomService;
    @Resource
    private SeatService seatService;

    @Resource
    private ThreadPoolExecutor executor;

    private static ReentrantLock lock;

    static {
        lock = new ReentrantLock(true);
    }

    /**
     * 该接口说明：
     * 1.该接口可供最高权限管理员、书记、辅导员账号使用
     * 2.模糊查询的key 可以模糊查看学院、姓名、班级、电话号码、年级
     * 3.若是最高权限管理员使用该接口，则不需要将学院、年级的参数传过来
     * 4.若是书记账号，则不需要将年级的参数传递过来
     * 5.若是辅导员账号，则需要将学院、所管理年级的参数传递过来
     * 6.该接口是显示已经注册成功的学生账号
     *
     * @param studentQueryReq 前端请求参数
     */
    @ApiOperation("不同等级管理员查询所有学生（带有模糊查询)")
    @PostMapping("/students")
    public Result findAll(@RequestBody @ApiParam("需要模糊查询的学生姓名") StudentQueryReq studentQueryReq) {
        LambdaQueryWrapper<Student> studentLambdaQueryWrapper = new LambdaQueryWrapper<>();
        studentLambdaQueryWrapper.eq(Student::getSStatus, 1);
        if (!ObjectUtils.isEmpty(studentQueryReq.getCollege())) {
            studentLambdaQueryWrapper.and(queryWrapper ->
                    queryWrapper.eq(Student::getSCollege, studentQueryReq.getCollege()));
        }
        if (!ObjectUtils.isEmpty(studentQueryReq.getGrade())) {
            studentLambdaQueryWrapper.and(queryWrapper ->
                    queryWrapper.eq(Student::getSGrade, studentQueryReq.getGrade()));
        }
        if (!ObjectUtils.isEmpty(studentQueryReq.getKey())) {

            studentLambdaQueryWrapper.and(queryWrapper ->
                    //学生姓名
                    queryWrapper.like(Student::getSName, studentQueryReq.getKey())
                            //学号
                            .or().like(Student::getSId, studentQueryReq.getKey())
                            //学院
                            .or().like(Student::getSCollege, studentQueryReq.getKey())
                            //年级
                            .or().like(Student::getSGrade, studentQueryReq.getKey())
                            //班别
                            .or().like(Student::getSClass, studentQueryReq.getKey())
                            //电话号码
                            .or().like(Student::getSPhone, studentQueryReq.getKey()));
        }
        List<Student> list = studentService.list(studentLambdaQueryWrapper);
        return new Result(true, list, "操作成功");
    }

    @PostMapping("/student/register")
    @Log(module = LogTypeEnum.Register)
    public Result register(@RequestBody Student student) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("s_id", student.getSId());
        Student checkStudent = studentService.checkLogin(map);
        if (checkStudent != null) {
            return new Result(false, 0, "注册失败，该学生已存在");
        }
        student.setSStatus("2");
        boolean save = studentService.save(student);
        if (save) {
            return new Result(true, 1, "注册成功");
        } else {
            return new Result(false, 0, "注册失败");
        }
    }

    // @ApiOperation("登录")
    // @PostMapping("/student/login")
    public Result login(@RequestBody Student student) {
        HashMap<String, Object> map = new HashMap<>(2);
        map.put("s_id", student.getSId());
        map.put("s_passWord", student.getSPassword());
        Student login = studentService.checkLogin(map);
        if (login != null && login.getSStatus().equals("1")) {
            return new Result(true, login, "登录成功");
        } else if (login != null && login.getSStatus().equals("2")) {
            return new Result(false, null, "该账号注册审核中，请耐心等待管理员审核");
        } else if (login != null) {
            return new Result(false, null, "该账号注册审核未通过，请联系管理员");
        }
        return new Result(false, null, "登录失败，请检查账号密码是否正确");
    }

    //返回值 1代表成功，2代表用户名不存在，3代表密码错误，4代表未通过审核

    @ApiOperation(value = "学生登录，如果登录成功，则根据该学生的预约状态返回响应的数据")
    @PostMapping("/student/login2")
    @Log(module = LogTypeEnum.Login)
    public Result studentLogin2(@RequestBody Student student, HttpSession session) {
        //TODO 接口响应到前端大约需要1s，需要优化
        long startTime = System.currentTimeMillis();

        //StudentBookingInfo bookingInfo = new StudentBookingInfo();
        ////获取该学生的预约信息
        //CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
        //    Booking booking = bookingService.getOne(new LambdaQueryWrapper<Booking>()
        //            .eq(Booking::getSId, student.getSId())
        //            .eq(Booking::getBUseful, 1));
        //    bookingInfo.setBId(booking.getBId())
        //            .setBStartTime(booking.getBStartTime())
        //            .setBEndTime(booking.getBEndTime());
        //    return booking;
        //}, executor).thenApply((booking) -> {
        //    //获取座位信息
        //    Seat seat = seatService.getById(booking.getSeatId());
        //    bookingInfo.setSeatNum(Integer.valueOf(seat.getSeatNum()));
        //    return seat;
        //}).thenAccept((seat) -> {
        //    //获取教室信息
        //    ClassRoom classRoom = classRoomService.getById(seat.getRId());
        //    bookingInfo
        //            .setRBuilding(classRoom.getRBuilding())
        //            .setRRoom(classRoom.getRRoom());
        //});


        LogStudentVo studentVo = new LogStudentVo();
        int b = studentService.checkStudent(student);
        if (b == 1) {
            boolean b1 = bookingService.queryStudentUserfulBookingInfo(student.getSId());
            if (b1) {
                StudentBookingInfo studentBookingInfo = studentService.queryUseFulStudentBookingInfo(student.getSId());
                BeanUtils.copyProperties(studentBookingInfo, studentVo);
                session.setAttribute("loginInfo", studentVo);
                log.info("controller的sessionId为：" + session);
                //CompletableFuture.allOf(completableFuture);

                //studentBookingInfo.setBId(bookingInfo.getBId())
                //        .setBStartTime(bookingInfo.getBStartTime())
                //        .setBEndTime(bookingInfo.getBEndTime())
                //        .setSeatNum(bookingInfo.getSeatNum())
                //        .setRBuilding(bookingInfo.getRBuilding())
                //        .setRRoom(bookingInfo.getRRoom());


                long endTime = System.currentTimeMillis();
                log.info("登录接口预约信息共花销{}", (endTime - startTime));
                return new Result(true, studentBookingInfo, "操作成功");
            } else {
                List<ClassRoom> roomsList = classRoomService.findUsefulClassRoomsList();
                HashMap<String, Object> map = new HashMap<>();
                map.put("s_id", student.getSId());
                Student student1 = studentService.checkLogin(map);
                StudentLoginNoBookingInfo info = new StudentLoginNoBookingInfo(roomsList, student1);
                session.setAttribute("loginInfo", studentVo);
                log.info("controller的sessionId为：" + session);
                return new Result(true, info, "操作成功");
            }
        } else if (b == 2) {
            return new Result(false, 2, "学号不存在");
        } else if (b == 3) {
            return new Result(false, 3, "密码错误");
        } else {
            return new Result(false, 4, "该学生未通过审核");
        }

    }

    @PostMapping("/checkStudent")
    public Result checkStudent(@RequestBody Student student) {
        boolean update = studentService.updateById(student);
        if (update) {
            return new Result(true, 1, "操作成功");
        }
        return new Result(false, 0, "操作失败");
    }

    @ApiOperation(value = "注册审核：查看所有学生的注册信息")
    @GetMapping("/student/findAllStudentRegisterInfo")
    public Result findAllStudentRegisterInfo(@RequestParam(required = false) String college) {
        List<Student> studentList = studentService.findAllStudentRegisterInfo(college);
        return new Result(true, studentList, "注册审核成功");

    }

    @ApiOperation(value = "注册审核：更新学生状态，即注册通过或注册失败")
    @PostMapping("/student/updateOrDeleteStudentStatus")
    @Log(module = LogTypeEnum.ExamineStudent)
    public Result updateOrDeleteStudentStatus(@RequestBody StudentCheckReq studentCheckReq) {
        Student student = new Student();
        student.setSStatus(studentCheckReq.getSStatus());
        student.setSId(studentCheckReq.getSId());
        int i = studentService.updateOrDeleteStudentStatus(student);
        if (i > 0) {
            return new Result(true, null, "操作成功");
        }
        return new Result(false, "null", "操作失败");
    }

    @ApiOperation(value = "给学生自动分配座位")
    @PostMapping("/student/provideStudentOneSeat")
    @Log(module = LogTypeEnum.SeatSelection)
    //@Transactional(rollbackFor = Exception.class)
    public Result provideStudentOneSeat(@RequestBody StudentReq studentReq) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-M-d");
        String currentTime = format.format(new Date());
        studentReq.setBStartTime(currentTime);
        studentReq.setBEndTime(format.format(new Date(new Date().getTime() + 6 * 24 * 60 * 60 * 1000)));
        //try {
        //    TimeUnit.SECONDS.sleep(10);
        //} catch (InterruptedException e) {
        //    throw new RuntimeException(e);
        //}
        lock.lock();
        //声明式事务开启
        TransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            log.error(Thread.currentThread().getName() + "\t获取锁进来了");
            boolean b = bookingService.queryStudentUserfulBookingInfo(studentReq.getSId());
            if (b) {
                transactionManager.commit(status);
                lock.unlock();
                return new Result(false, "null", "预约失败 该学生已经有了预约记录");
            }
            seatService.provideOneSeatForStudent(studentReq);
            boolean b1 = bookingService.queryStudentUserfulBookingInfo(studentReq.getSId());
            if (b1) {
                StudentBookingInfo studentBookingInfo = studentService.queryUseFulStudentBookingInfo(studentReq.getSId());
                transactionManager.commit(status);
                lock.unlock();
                return new Result(true, studentBookingInfo, "操作成功");
            }
            transactionManager.commit(status);
            lock.unlock();
            return new Result(false, "null", "操作失败");
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw new RuntimeException(e);
        } finally {
            if (null != status && !status.isCompleted()) {
                transactionManager.rollback(status);
            }
        }

    }

    // @ApiOperation(value = "更新学生信息")
    // @PostMapping("/student/updateStudentInfo")
    // public Result updateStudentInfo(@RequestBody Student student){
    //     boolean b = studentService.updateStudentInfo(student);
    //     if(b){
    //         return new Result(true,1,"操作成功");
    //     }
    //     return new Result(false,0,"操作失败，可能改学生不存在");
    // }


    @ApiOperation("更新学生信息")
    @PostMapping("/student/updateStudentInfo")
    @Log(module = LogTypeEnum.UpdateInfo)
    public Result updateStudentInfo(@RequestBody Student student) {
        LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Student::getSId, student.getSId());
        boolean b = studentService.update(student, wrapper);
        if (b) {
            Student serviceOne = studentService.getOne(wrapper);
            return new Result(true, serviceOne, "操作成功");
        }
        return new Result(false, 0, "操作失败，可能改学生不存在");
    }


    @PutMapping("/student/updatePassword")
    @ApiOperation("修改学生密码")
    @Log(module = LogTypeEnum.UpdatePassword)
    public Result updatePassword(@RequestBody StudentPasswordReq studentPasswordReq) {
        if (ObjectUtils.isEmpty(studentPasswordReq)) {
            return new Result(false, null, "参数错误");
        }
        LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Student::getSId, studentPasswordReq.getSId());
        Student student = new Student();
        student.setSPassword(studentPasswordReq.getSPassword());
        boolean update = studentService.update(student, wrapper);
        if (update) {
            return new Result(true, 1, "更新成功");
        }
        return new Result(false, null, "更新失败");
    }

    @DeleteMapping("student/delete/{sId}")
    @ApiOperation("删除学生")
    public Result delete(@PathVariable("sId") @ApiParam("学号") String sId) {
        studentService.remove(new LambdaQueryWrapper<Student>().eq(Student::getSId, sId));
        return new Result(true, null, "操作成功");
    }

    @PutMapping("/student/reset/{sId}")
    @ApiOperation("重置密码，重置为123456")
    @Log(module = LogTypeEnum.ResetPassword)
    public Result reset(@PathVariable("sId") @ApiParam("学号") String sId) {
        Student student = new Student();
        student.setSId(sId);
        student.setSPassword(MD5Util.md5("123456"));
        System.out.println(student);
        studentService.update(student, new LambdaQueryWrapper<Student>().eq(Student::getSId, sId));
        return new Result(true, null, "重置密码成功");
    }


}
